home *** CD-ROM | disk | FTP | other *** search
/ Delphi Informant Complete 1995 - 2000 / Delphi Informant Complete 1995 to 2000.iso / Delphi Informant Magazine Complete Works SOURCE CODE 1998.rar / 1998 / Aug / di9808rs / PLATONIF.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1998-03-18  |  17.0 KB  |  591 lines

  1. unit PlatoniF;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  7.   Math, StdCtrls, ExtCtrls, ComCtrls;
  8.  
  9. const
  10.     Xmin = -10;
  11.     Xmax = 10;
  12.     Ymin = -10;
  13.     Ymax = 10;
  14.  
  15. type
  16.     TMatrix3D = array [1..4, 1..4] of Single;
  17.     TVector3D = array [1..4] of Single;
  18.     TPoint3D = record
  19.         Coord : TVector3D; // The untransformed coordinates.
  20.         Trans : TVector3D; // The transformed coordinates.
  21.     end;
  22.  
  23.   TPlatonicForm = class(TForm)
  24.     SolidOption: TRadioGroup;
  25.     ShowAxesCheck: TCheckBox;
  26.     RotateLeftRight: TUpDown;
  27.     RotateUpDown: TUpDown;
  28.     procedure FormCreate(Sender: TObject);
  29.     procedure MakeIdentity(var M : TMatrix3D);
  30.     procedure MatrixMatrixMult(var R : TMatrix3D; A, B : TMatrix3D);
  31.     procedure VectorMatrixMult(var r : TVector3D; p : TVector3D; A : TMatrix3D);
  32.     procedure FormPaint(Sender: TObject);
  33.     procedure BuildTransformation(var T : TMatrix3D);
  34.     procedure AddSegment(x1, y1, z1, x2, y2, z2 : Single);
  35.     function SidesUneven(seg1, seg2 : Integer) : Boolean;
  36.     procedure ShowMatrix(M : TMatrix3D);
  37.     procedure FormKeyDown(Sender: TObject; var Key: Word;
  38.       Shift: TShiftState);
  39.     procedure SolidOptionClick(Sender: TObject);
  40.     procedure ShowAxesCheckClick(Sender: TObject);
  41.     procedure RotateLeftRightClick(Sender: TObject; Button: TUDBtnType);
  42.     procedure RotateUpDownClick(Sender: TObject; Button: TUDBtnType);
  43.  
  44.   private
  45.     { Private declarations }
  46.     EyeX, EyeY, EyeZ : Single; // Coordinates of viewing eye.
  47.  
  48.     // Line segment coordinates.
  49.     Segments     : array [0..1000, 1..2] of TPoint3D;
  50.     NumSegments  : Integer;
  51.  
  52.     // Index of first segment in each polytope.
  53.     FirstSegment : array[0..6] of Integer;
  54.  
  55.   public
  56.     { Public declarations }
  57.   end;
  58.  
  59. var
  60.   PlatonicForm: TPlatonicForm;
  61.  
  62. implementation
  63.  
  64. {$R *.DFM}
  65.  
  66. // Create some data to display.
  67. procedure TPlatonicForm.FormCreate(Sender: TObject);
  68. var
  69.     theta1, theta2, s1, s2, c1, c2      : Single;
  70.     S, R, H, A, B, C, D, x, y, y2, M, N : Single;
  71. begin
  72.     // Initialize the viewing eye
  73.     EyeX := 40;
  74.     EyeY := 20;
  75.     EyeZ := 20;
  76.  
  77.     // Initialize the segment data.
  78.     NumSegments := 0;
  79.  
  80.     // Save segments for the axes.
  81.     FirstSegment[0] := NumSegments;
  82.     AddSegment(0, 0, 0, 2, 0, 0); // X axis.
  83.     AddSegment(0, 0, 0, 0, 1, 0); // Y axis.
  84.     AddSegment(0, 0, 0, 0, 0, 1); // Z axis.
  85.  
  86.     // Tetrahedron.
  87.     FirstSegment[1] := NumSegments;
  88.     S := Sqrt(6);
  89.     A := S / Sqrt(3);
  90.     B := -A / 2;
  91.     C := A * Sqrt(2) - 1;
  92.     D := S / 2;
  93.     AddSegment(0, C, 0, A, -1, 0);
  94.     AddSegment(0, C, 0, B, -1, D);
  95.     AddSegment(0, C, 0, B, -1, -D);
  96.     AddSegment(B, -1, -D, B, -1, D);
  97.     AddSegment(B, -1, D, A, -1, 0);
  98.     AddSegment(A, -1, 0, B, -1, -D);
  99.  
  100.     // Cube.
  101.     FirstSegment[2] := NumSegments;
  102.     AddSegment(-1, -1, -1, -1, 1, -1);
  103.     AddSegment(-1, 1, -1, 1, 1, -1);
  104.     AddSegment(1, 1, -1, 1, -1, -1);
  105.     AddSegment(1, -1, -1, -1, -1, -1);
  106.  
  107.     AddSegment(-1, -1, 1, -1, 1, 1);
  108.     AddSegment(-1, 1, 1, 1, 1, 1);
  109.     AddSegment(1, 1, 1, 1, -1, 1);
  110.     AddSegment(1, -1, 1, -1, -1, 1);
  111.  
  112.     AddSegment(-1, -1, -1, -1, -1, 1);
  113.     AddSegment(-1, 1, -1, -1, 1, 1);
  114.     AddSegment(1, 1, -1, 1, 1, 1);
  115.     AddSegment(1, -1, -1, 1, -1, 1);
  116.  
  117.     // Octahedron.
  118.     FirstSegment[3] := NumSegments;
  119.     AddSegment(0, 1, 0, 1, 0, 0);
  120.     AddSegment(0, 1, 0, -1, 0, 0);
  121.     AddSegment(0, 1, 0, 0, 0, 1);
  122.     AddSegment(0, 1, 0, 0, 0, -1);
  123.  
  124.     AddSegment(0, -1, 0, 1, 0, 0);
  125.     AddSegment(0, -1, 0, -1, 0, 0);
  126.     AddSegment(0, -1, 0, 0, 0, 1);
  127.     AddSegment(0, -1, 0, 0, 0, -1);
  128.  
  129.     AddSegment(0, 0, 1, 1, 0, 0);
  130.     AddSegment(0, 0, 1, -1, 0, 0);
  131.     AddSegment(0, 0, -1, 1, 0, 0);
  132.     AddSegment(0, 0, -1, -1, 0, 0);
  133.  
  134.     // Dodecahedron.
  135.     FirstSegment[4] := NumSegments;
  136.     theta1 := PI * 0.4;
  137.     theta2 := PI * 0.8;
  138.     s1 := Sin(theta1);
  139.     c1 := Cos(theta1);
  140.     s2 := Sin(theta2);
  141.     c2 := Cos(theta2);
  142.  
  143.     M := 1 - (2 - 2 * c1 - 4 * s1 * s1) / (2 * c1 - 2);
  144.     N := Sqrt((2 - 2 * c1) - M * M) * (1 + (1 - c2) / (c1 - c2));
  145.     R := 2 / N;
  146.     S := R * Sqrt(2 - 2 * c1);
  147.     A := R * s1;
  148.     B := R * s2;
  149.     C := R * c1;
  150.     D := R * c2;
  151.  
  152.     x := (R * R * (2 - 2 * c1) - 4 * A * A) / (2 * C - 2 * R);
  153.     y := Sqrt(S * S - (R - x) * (R - x));
  154.     y2 := y * (1 - c2) / (c1 - c2);
  155.  
  156.     AddSegment(R, 1, 0, C, 1, A);        // Top
  157.     AddSegment(C, 1, A, D, 1, B);
  158.     AddSegment(D, 1, B, D, 1, -B);
  159.     AddSegment(D, 1, -B, C, 1, -A);
  160.     AddSegment(C, 1, -A, R, 1, 0);
  161.  
  162.     AddSegment(R, 1, 0, x, 1 - y, 0);    // Top downward edges.
  163.     AddSegment(C, 1, A, x * c1, 1 - y, x * s1);
  164.     AddSegment(C, 1, -A, x * c1, 1 - y, -x * s1);
  165.     AddSegment(D, 1, B, x * c2, 1 - y, x * s2);
  166.     AddSegment(D, 1, -B, x * c2, 1 - y, -x * s2);
  167.  
  168.     AddSegment(x, 1 - y, 0, -x * c2, 1 - y2, -x * s2);   // Middle.
  169.     AddSegment(x, 1 - y, 0, -x * c2, 1 - y2, x * s2);
  170.     AddSegment(x * c1, 1 - y, x * s1, -x * c2, 1 - y2, x * s2);
  171.     AddSegment(x * c1, 1 - y, x * s1, -x * c1, 1 - y2, x * s1);
  172.     AddSegment(x * c2, 1 - y, x * s2, -x * c1, 1 - y2, x * s1);
  173.     AddSegment(x * c2, 1 - y, x * s2, -x, 1 - y2, 0);
  174.     AddSegment(x * c2, 1 - y, -x * s2, -x, 1 - y2, 0);
  175.     AddSegment(x * c2, 1 - y, -x * s2, -x * c1, 1 - y2, -x * s1);
  176.     AddSegment(x * c1, 1 - y, -x * s1, -x * c1, 1 - y2, -x * s1);
  177.     AddSegment(x * c1, 1 - y, -x * s1, -x * c2, 1 - y2, -x * s2);
  178.  
  179.     AddSegment(-R, -1, 0, -x, 1 - y2, 0);    // Bottom upward edges.
  180.     AddSegment(-C, -1, A, -x * c1, 1 - y2, x * s1); // Bottom upward edges.
  181.     AddSegment(-D, -1, B, -x * c2, 1 - y2, x * s2);
  182.     AddSegment(-D, -1, -B, -x * c2, 1 - y2, -x * s2);
  183.     AddSegment(-C, -1, -A, -x * c1, 1 - y2, -x * s1);
  184.  
  185.     AddSegment(-R, -1, 0, -C, -1, A);    // Bottom
  186.     AddSegment(-C, -1, A, -D, -1, B);
  187.     AddSegment(-D, -1, B, -D, -1, -B);
  188.     AddSegment(-D, -1, -B, -C, -1, -A);
  189.     AddSegment(-C, -1, -A, -R, -1, 0);
  190.  
  191.     // Icosahedron.
  192.     FirstSegment[5] := NumSegments;
  193.     R := 2 / (2 * Sqrt(1 - 2 * c1) + Sqrt(3 / 4 * (2 - 2 * c1) - 2 * c2 - c2 * c2 - 1));
  194.     S := R * Sqrt(2 - 2 * c1);
  195.     H := 1 - Sqrt(S * S - R * R);
  196.     A := R * s1;
  197.     B := R * s2;
  198.     C := R * c1;
  199.     D := R * c2;
  200.     AddSegment(R, H, 0, C, H, A);        // Top
  201.     AddSegment(C, H, A, D, H, B);
  202.     AddSegment(D, H, B, D, H, -B);
  203.     AddSegment(D, H, -B, C, H, -A);
  204.     AddSegment(C, H, -A, R, H, 0);
  205.     AddSegment(R, H, 0, 0, 1, 0);        // Point
  206.     AddSegment(C, H, A, 0, 1, 0);
  207.     AddSegment(D, H, B, 0, 1, 0);
  208.     AddSegment(D, H, -B, 0, 1, 0);
  209.     AddSegment(C, H, -A, 0, 1, 0);
  210.  
  211.     AddSegment(-R, -H, 0, -C, -H, A);    // Bottom
  212.     AddSegment(-C, -H, A, -D, -H, B);
  213.     AddSegment(-D, -H, B, -D, -H, -B);
  214.     AddSegment(-D, -H, -B, -C, -H, -A);
  215.     AddSegment(-C, -H, -A, -R, -H, 0);
  216.     AddSegment(-R, -H, 0, 0, -1, 0);     // Point
  217.     AddSegment(-C, -H, A, 0, -1, 0);
  218.     AddSegment(-D, -H, B, 0, -1, 0);
  219.     AddSegment(-D, -H, -B, 0, -1, 0);
  220.     AddSegment(-C, -H, -A, 0, -1, 0);
  221.  
  222.     AddSegment(R, H, 0, -D, -H, B);      // Middle
  223.     AddSegment(R, H, 0, -D, -H, -B);
  224.     AddSegment(C, H, A, -D, -H, B);
  225.     AddSegment(C, H, A, -C, -H, A);
  226.     AddSegment(D, H, B, -C, -H, A);
  227.     AddSegment(D, H, B, -R, -H, 0);
  228.     AddSegment(D, H, -B, -R, -H, 0);
  229.     AddSegment(D, H, -B, -C, -H, -A);
  230.     AddSegment(C, H, -A, -C, -H, -A);
  231.     AddSegment(C, H, -A, -D, -H, -B);
  232.     FirstSegment[6] := NumSegments;
  233.  
  234.     // Verify that the side lengths are all the same.
  235.     if (SidesUneven(FirstSegment[1], FirstSegment[2] - 1)) then ShowMessage('Error in tetrahedron.');
  236.     if (SidesUneven(FirstSegment[2], FirstSegment[3] - 1)) then ShowMessage('Error in cube.');
  237.     if (SidesUneven(FirstSegment[3], FirstSegment[4] - 1)) then ShowMessage('Error in octahedron.');
  238.     if (SidesUneven(FirstSegment[4], FirstSegment[5] - 1)) then ShowMessage('Error in dodecahedron.');
  239.     if (SidesUneven(FirstSegment[5], FirstSegment[6] - 1)) then ShowMessage('Error in icosahedron.');
  240. end;
  241.  
  242. // Add a segment to the list.
  243. procedure TPlatonicForm.AddSegment(x1, y1, z1, x2, y2, z2 : Single);
  244. begin
  245.     with Segments[NumSegments, 1] do
  246.     begin
  247.         Coord[1] := x1;
  248.         Coord[2] := y1;
  249.         Coord[3] := z1;
  250.         Coord[4] := 1.0;
  251.     end;
  252.     with Segments[NumSegments, 2] do
  253.     begin
  254.         Coord[1] := x2;
  255.         Coord[2] := y2;
  256.         Coord[3] := z2;
  257.         Coord[4] := 1.0;
  258.     end;
  259.     NumSegments := NumSegments + 1;
  260. end;
  261.  
  262. // Return True if the bounded segments do not all have
  263. // the same length.
  264. function TPlatonicForm.SidesUneven(seg1, seg2 : Integer) : Boolean;
  265.  
  266.     function SegmentLength(seg : Integer) : Single;
  267.     var
  268.         x1, y1, z1, x2, y2, z2, dx, dy, dz : Single;
  269.     begin
  270.         with Segments[seg1, 1] do
  271.         begin
  272.             x1 := Coord[1];
  273.             y1 := Coord[2];
  274.             z1 := Coord[3];
  275.         end;
  276.         with Segments[seg1, 2] do
  277.         begin
  278.             x2 := Coord[1];
  279.             y2 := Coord[2];
  280.             z2 := Coord[3];
  281.         end;
  282.         dx := x2 - x1;
  283.         dy := y2 - y1;
  284.         dz := z2 - z1;
  285.         Result := Sqrt(dx * dx + dy * dy + dz * dz);
  286.     end;
  287.  
  288. var
  289.     len : Single;
  290.     i   : Integer;
  291. begin
  292.     // Get the first segment's length.
  293.     len := SegmentLength(seg1);
  294.  
  295.     // Compare this to the lengths of the other segments.
  296.     for i := seg1 + 1 to seg2 do
  297.         if (Abs(SegmentLength(i) - len) > 0.1) then
  298.         begin
  299.             Result := True;
  300.             exit;
  301.         end;
  302.  
  303.     Result := False;
  304. end;
  305.  
  306. // Make M an identity matrix.
  307. procedure TPlatonicForm.MakeIdentity(var M : TMatrix3D);
  308. var
  309.     i, j : Integer;
  310. begin
  311.     for i := 1 to 4 do
  312.         for j := 1 to 4 do
  313.             if (i = j) then
  314.                 M[i, j] := 1.0
  315.             else
  316.                 M[i, j] := 0.0;
  317. end;
  318.  
  319. // Perform matrix-matrix multiplication. Set R = A * B.
  320. procedure TPlatonicForm.MatrixMatrixMult(var R : TMatrix3D; A, B : TMatrix3D);
  321. var
  322.     i, j, k : Integer;
  323.     value   : Single;
  324. begin
  325.     for i := 1 to 4 do
  326.         for j := 1 to 4 do
  327.         begin
  328.             // Calculate R[i, j].
  329.             value := 0.0;
  330.             for k := 1 to 4 do
  331.                 value := value + A[i, k] * B[k, j];
  332.             R[i, j] := value;
  333.         end;
  334. end;
  335.  
  336. // Perform vector-matrix multiplication. Set r = p * A.
  337. procedure TPlatonicForm.VectorMatrixMult(var r : TVector3D; p : TVector3D; A : TMatrix3D);
  338. var
  339.     i, j  : Integer;
  340.     value : Single;
  341. begin
  342.     for i := 1 to 4 do
  343.     begin
  344.         value := 0.0;
  345.         for j := 1 to 4 do
  346.             value := value + p[j] * A[j, i];
  347.         r[i] := value;
  348.     end;
  349.  
  350.     // normalize the point. Note value still holds r[4].
  351.     r[1] := r[1] / value;
  352.     r[2] := r[2] / value;
  353.     r[3] := r[3] / value;
  354.     r[4] := 1.0;
  355. end;
  356.  
  357. // Draw the selected solid.
  358. procedure TPlatonicForm.FormPaint(Sender: TObject);
  359. var
  360.     i, seg1, seg2 : Integer;
  361.     T             : TMatrix3D;
  362.     rect          : TRect;
  363. begin
  364.     // Build the transformation.
  365.     BuildTransformation(T);
  366.  
  367.     // Erase the form.
  368.     rect.Left := 0;
  369.     rect.Top := 0;
  370.     rect.Right := ClientWidth;
  371.     rect.Bottom := ClientHeight;
  372.     Canvas.Brush.Color := Color;
  373.     Canvas.FillRect(rect);
  374.  
  375.     // Draw the selected solid's segments.
  376.     Canvas.Pen.Color := clBlack;
  377.     i := SolidOption.ItemIndex + 1;
  378.     seg1 := FirstSegment[i];
  379.     seg2 := FirstSegment[i + 1] - 1;
  380.     for i := seg1 to seg2 do
  381.     begin
  382.         // Apply the transformation to the points.
  383.         VectorMatrixMult(Segments[i, 1].Trans,
  384.                          Segments[i, 1].Coord, T);
  385.         VectorMatrixMult(Segments[i, 2].Trans,
  386.                          Segments[i, 2].Coord, T);
  387.  
  388.         // Draw the segment.
  389.         Canvas.MoveTo(Round(Segments[i, 1].Trans[1]),
  390.                       Round(Segments[i, 1].Trans[2]));
  391.         Canvas.LineTo(Round(Segments[i, 2].Trans[1]),
  392.                       Round(Segments[i, 2].Trans[2]));
  393.     end;
  394.  
  395.     // Draw the axes if desired.
  396.     if (ShowAxesCheck.Checked) then
  397.     begin
  398.         Canvas.Pen.Color := clGreen;
  399.         for i := FirstSegment[0] to FirstSegment[1] - 1 do
  400.         begin
  401.             // Apply the transformation to the points.
  402.             VectorMatrixMult(Segments[i, 1].Trans,
  403.                              Segments[i, 1].Coord, T);
  404.             VectorMatrixMult(Segments[i, 2].Trans,
  405.                              Segments[i, 2].Coord, T);
  406.  
  407.             // Draw the segments.
  408.             Canvas.MoveTo(Round(Segments[i, 1].Trans[1]),
  409.                           Round(Segments[i, 1].Trans[2]));
  410.             Canvas.LineTo(Round(Segments[i, 2].Trans[1]),
  411.                           Round(Segments[i, 2].Trans[2]));
  412.         end;
  413.     end;
  414. end;
  415.  
  416. // Build a transformation matrix for display.
  417. procedure TPlatonicForm.BuildTransformation(var T : TMatrix3D);
  418. var
  419.     r1, r2, ctheta, stheta, cphi, sphi : Single;
  420.     T1, T2, T3, T4, T12, T34           : TMatrix3D;
  421. begin
  422.     // Rotate around the Z axis until the eye lies in
  423.     // the Y-Z plane.
  424.     r1 := Sqrt(EyeX * EyeX + EyeY * EyeY);
  425.     stheta := EyeX / r1;
  426.     ctheta := EyeY / r1;
  427.     MakeIdentity(T1);
  428.     T1[1, 1] := ctheta;
  429.     T1[1, 2] := stheta;
  430.     T1[2, 1] := -stheta;
  431.     T1[2, 2] := ctheta;
  432.  
  433.     // Rotate around the X axis until the eye lies within
  434.     // the Z axis.
  435.     r2 := Sqrt(EyeX * EyeX + EyeY * EyeY + EyeZ * EyeZ);
  436.     sphi := -r1 / r2;
  437.     cphi := -EyeZ / r2;
  438.     MakeIdentity(T2);
  439.     T2[2, 2] := cphi;
  440.     T2[2, 3] := sphi;
  441.     T2[3, 2] := -sphi;
  442.     T2[3, 3] := cphi;
  443.  
  444.     // We could project along the Z axis here. Instead we
  445.     // just ignore the Z coordinate when drawing.
  446.  
  447.     // Make the picture reasonably large on the form.
  448.     // Here we scale y by -50 to reverse its sign since
  449.     // the Canvas starts with (0, 0) in the upper left.
  450.     MakeIdentity(T3);
  451.     T3[1, 1] := 50;
  452.     T3[2, 2] := -50;
  453.     T3[3, 3] := 50;
  454.  
  455.     // Center the picture on the form.
  456.     MakeIdentity(T4);
  457.     r1 := SolidOption.Width + SolidOption.Left;
  458.     T4[4, 1] := (ClientWidth - r1) / 2 + r1;
  459.     T4[4, 2] := ClientHeight / 2;
  460.  
  461.     // Combine the transformations.
  462.     MatrixMatrixMult(T12, T1, T2);
  463.     MatrixMatrixMult(T34, T3, T4);
  464.     MatrixMatrixMult(T, T12, T34);
  465. end;
  466.  
  467. // This procedure is useful for debugging.
  468. procedure TPlatonicForm.ShowMatrix(M : TMatrix3D);
  469. var
  470.     i, j : Integer;
  471.     txt  : String;
  472. begin
  473.     txt := '';
  474.     for i := 1 to 4 do
  475.     begin
  476.         for j := 1 to 4 do
  477.             txt := txt + Format('%4.2f ', [M[i, j]]);
  478.         txt := txt + #10#13;
  479.     end;
  480.     ShowMessage(txt);
  481. end;
  482.  
  483. // Adjust the eye position if it's an arrow key.
  484. procedure TPlatonicForm.FormKeyDown(Sender: TObject; var Key: Word;
  485.   Shift: TShiftState);
  486. const
  487.     PI = 3.14159;
  488.     Dtheta = PI / 16;
  489.     Dphi = PI / 16;
  490. var
  491.     theta, phi, r1, r2 : Single;
  492. begin
  493.     if (not (Key in [VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT]))
  494.         then exit;
  495.  
  496.     theta := ArcTan2(EyeY, EyeX);
  497.     r1 := Sqrt(EyeX * EyeX + EyeY * EyeY);
  498.     r2 := Sqrt(EyeX * EyeX + EyeY * EyeY + EyeZ * EyeZ);
  499.     phi := ArcTan2(EyeZ, r1);
  500.  
  501.     case Key of
  502.         VK_LEFT:
  503.             theta := theta - Dtheta;
  504.  
  505.         VK_UP:
  506.             begin
  507.                 phi := phi + Dphi;
  508.                 if (phi > PI / 2) then phi := PI / 2;
  509.             end;
  510.  
  511.         VK_RIGHT:
  512.             theta := theta + Dtheta;
  513.  
  514.         VK_DOWN:
  515.             begin
  516.                 phi := phi - Dphi;
  517.                 if (phi < -PI / 2) then phi := -PI / 2;
  518.             end;
  519.     end;
  520.  
  521.     EyeX := r1 * Cos(theta);
  522.     EyeY := r1 * Sin(theta);
  523.     EyeZ := r2 * Sin(phi);
  524.     Refresh;
  525. end;
  526.  
  527. // Redraw to show the new selection.
  528. procedure TPlatonicForm.SolidOptionClick(Sender: TObject);
  529. begin
  530.     Refresh;
  531. end;
  532.  
  533. // Redraw to show the new selection.
  534. procedure TPlatonicForm.ShowAxesCheckClick(Sender: TObject);
  535. begin
  536.     Refresh;
  537. end;
  538.  
  539. procedure TPlatonicForm.RotateLeftRightClick(Sender: TObject;
  540.   Button: TUDBtnType);
  541. const
  542.     PI = 3.14159;
  543.     Dtheta = PI / 16;
  544. var
  545.     theta, r1 : Single;
  546. begin
  547.     theta := ArcTan2(EyeY, EyeX);
  548.     r1 := Sqrt(EyeX * EyeX + EyeY * EyeY);
  549.  
  550.     if (Button = btNext) then
  551.         // Right.
  552.         theta := theta + Dtheta
  553.     else
  554.         // Left.
  555.         theta := theta - Dtheta;
  556.  
  557.     EyeX := r1 * Cos(theta);
  558.     EyeY := r1 * Sin(theta);
  559.     Refresh;
  560. end;
  561.  
  562. procedure TPlatonicForm.RotateUpDownClick(Sender: TObject;
  563.   Button: TUDBtnType);
  564. const
  565.     PI = 3.14159;
  566.     Dphi = PI / 16;
  567. var
  568.     theta, phi, r1, r2 : Single;
  569. begin
  570.     theta := ArcTan2(EyeY, EyeX);
  571.     r1 := Sqrt(EyeX * EyeX + EyeY * EyeY);
  572.     r2 := Sqrt(EyeX * EyeX + EyeY * EyeY + EyeZ * EyeZ);
  573.     phi := ArcTan2(EyeZ, r1);
  574.  
  575.     if (Button = btNext) then
  576.     begin
  577.         phi := phi + Dphi;
  578.         if (phi > PI / 2) then phi := PI / 2;
  579.     end else begin
  580.         phi := phi - Dphi;
  581.         if (phi < -PI / 2) then phi := -PI / 2;
  582.     end;
  583.  
  584.     EyeX := r1 * Cos(theta);
  585.     EyeY := r1 * Sin(theta);
  586.     EyeZ := r2 * Sin(phi);
  587.     Refresh;
  588. end;
  589.  
  590. end.
  591.